//
//  TrainingData.cpp
//  RandomForest-AndreaBalboni
//
//  Created by Andrea Balboni on 05/04/12.
//  Copyright (c) 2012 Università degli Studi di Modena e Reggio Emilia. All rights reserved.
//

#include <iostream>
#include <sstream>
#include <fstream>
#include <algorithm>
#include <map>
#include <cmath>
#include "TrainingData.h"


//Restituisce la dimensione del dataset
template <typename _T>
int TrainingData<_T>::size() {
    return _trainingData.size();
}

//Imposta la dimensione del dataset
template <typename _T>
void TrainingData<_T>::resize(int size){
    _trainingData.resize(size);
}

//Restituisce la dimensionalita' delle features del dataset
template <typename _T>
unsigned int TrainingData<_T>::getFeatureSpaceDimension() {
    return _featureSpaceDimension;
}

//Imposta la dimensionalità delle features del dataset
template <typename _T>
void TrainingData<_T>::setFeatureSpaceDimension(unsigned int dimensionality) {
     _featureSpaceDimension=dimensionality;
}

//Restituisce un record del dataset alla data posizione
template <typename _T>
MyRecord<_T>& TrainingData<_T>::getRecord(unsigned int index){
    return _trainingData.at(index);
}

//Restituisce un record del dataset alla data posizione in una matrice opencv
template <typename _T>
void TrainingData<_T>::getRecordMat(unsigned int index, cv::Mat_<double> &r){
    r=cv::Mat_<double>::zeros(getFeatureSpaceDimension(),1);
    for(unsigned int i=0;i<getFeatureSpaceDimension();++i)
        r(i)=_trainingData.at(index)(i);
}

//Aggiunge un record alla fine del dataset
template <typename _T>
void TrainingData<_T>::push_Record(MyRecord<_T> &record){
    _trainingData.push_back(record);
}


//Legge una stringa da file fermandosi ad alcuni delimitatori
template <typename _T>
std::string TrainingData<_T>::readStringVal(std::ifstream &file){
    std::string s;
	char buf;
	do {
		file.get(buf);
		if (file.eof() || buf=='\n' || buf==' ' || buf=='\t')
			break;
		s.append (1,buf);
	} while (true);
	return s;
    
}

//Legge un valore da file e lo restituisce
template <typename _T>
_T TrainingData<_T>::readVal(std::ifstream &file){
    std::string s;
	s=readStringVal(file);
    std::stringstream stream(s);
	_T val;
	stream >> val;
	return val;
    
}

//Legge l'intero dataset da file e lo inserisce nell'apposita struttura dati
template <typename _T> 
bool TrainingData<_T>::readData(std::string filename) {
    std::ifstream file(filename.c_str());
    if(!file){
        std::cerr<<"Error opening "<<filename<<" , try again";
        return false;
    }
    float elem=-1.0;
    
    unsigned int spaceDimension;
    file >> elem;
    if(file.fail() || file.eof()){
        std::cerr<<"Errore nella lettura della dimensionalita' delle features"<<std::endl;
        return false;
    }
    spaceDimension=(unsigned int) elem;
    setFeatureSpaceDimension(spaceDimension);
    int count=0;
    MyRecord<_T> record(spaceDimension);
    while (true) {
        for (unsigned i=0; i<getFeatureSpaceDimension();++i){
            file>>elem;
            if(file.fail() || file.eof()){
                break;
            }
            record(i)=(_T)elem;
        }
        if(file.fail() || file.eof())
            break;
        int classe=-1;
        file >> elem;
        if(file.fail() || file.eof()){
            std::cerr<<"Errore nella lettura di un valore di classe per il record "<<count+1<<std::endl;
            return false;
        }
            
        classe=(int) elem;
        record.setClass(classe);
        push_Record(record);
        count++;
        
        char buff;
        do
        {
            file.get(buff);
        }
        while(buff !='\n');
    }
    file.close();
    return true;
}

//Legge un record da file
template <typename _T>
MyRecord<_T> TrainingData<_T>::readTrainingRecord(std::ifstream &file, int nfeatures, int nclasses) {
    MyRecord<_T> record;
    std::vector<_T> features;
    
    for(unsigned i=0;i<nfeatures;++i){
        _T val=TrainingData<_T>::readVal(file);
        features.push_back(val);
    }
    typedef typename std::vector<_T>::iterator iterv;
    //    std::cout<<features.size()<<std::endl;
    //    for(iterv it=features.begin();it!=features.end();++it){
    //        std::cout<<*it<<std::endl;
    //    }
    record.setFeatureVector(features);
    for(unsigned i=0;i<nclasses;++i){
        int cl=TrainingData<_T>::readVal(file);
        if(cl!=0){
            record.setClass((int)i);
            break;
        }
    }
    
    return record;
}

//Restituisce il valore massimo tra le features di tutti i record del dataset in posizione data
template <typename _T>
_T TrainingData<_T>::getMaxValAtFeatureIndex(unsigned int featureIndex) {
    _T maxVal;
    std::vector<_T> valuesAtIndex;
    valuesAtIndex.clear();
    valuesAtIndex.resize(this->size());
    VectorRecordIterator it=_trainingData.begin();
    for(; it<_trainingData.end(); ++it){
        valuesAtIndex.push_back(it->getFeature(featureIndex));
        //std::cout<<it->getFeature(featureIndex)<<std::endl;
    }
    _TVectorIterator iter=max_element(valuesAtIndex.begin(),valuesAtIndex.end());
    maxVal=*iter;
    return maxVal;
}

//Restituisce il valore minimo tra le features di tutti i record del dataset in posizione data
template <typename _T>
_T TrainingData<_T>::getMinValAtFeatureIndex(unsigned int featureIndex) {
    _T minVal;
    std::vector<_T> valuesAtIndex;
    valuesAtIndex.clear();
    valuesAtIndex.resize(this->size());
    VectorRecordIterator it=this->_trainingData.begin();
    for(; it!=_trainingData.end(); ++it){
        valuesAtIndex.push_back(it->getFeature(featureIndex));
        //std::cout<<it->getFeature(featureIndex)<<std::endl;
    }
    _TVectorIterator iter=min_element(valuesAtIndex.begin(),valuesAtIndex.end());
    minVal=*iter;
    return minVal;
}

template <typename _T>
void TrainingData<_T>::getMinMaxValAtFeatureIndex(unsigned int featureIndex, _T &minVal, _T &maxVal) {
    std::vector<_T> valuesAtIndex;
    valuesAtIndex.clear();
    valuesAtIndex.resize(this->size());
    VectorRecordIterator it=this->_trainingData.begin();
    for(; it!=_trainingData.end(); ++it){
        valuesAtIndex.push_back(it->getFeature(featureIndex));
    }
    //Non uso il sort perche' e' lento
    //sort(valuesAtIndex.begin(),valuesAtIndex.end());
    minVal=*min_element(valuesAtIndex.begin(),valuesAtIndex.end());
    maxVal=*max_element(valuesAtIndex.begin(),valuesAtIndex.end());
}

/**
 * Ritorna nel vettore v il valore della feature alla dimensione D per ogni 
 * record puntato dall'indice i nel vettore indexes 
 */
template <typename _T> void TrainingData<_T>::getFeatureValueOnDimensionD(	std::vector<std::pair<_T, unsigned int> > &v, 
                                                                          std::vector<unsigned int> &indexes, 
                                                                          unsigned int splitDim) {
    
	std::vector<unsigned int>::iterator it = indexes.begin();
	for(; it!=indexes.end(); it++) 
		v.push_back(std::pair<_T,unsigned int>(this->getRecord(*it)(splitDim), this->getRecord(*it).getClass()));
    
    
}




//Calcolo la distribuzione delle classi nel dataset
template <typename _T>
std::map<int,double> TrainingData<_T>::classProbDist() {
    std::map<int,double> h;
    typedef typename std::map<int,double>::iterator hiter;
    int numrecords=size();
    for(VectorRecordIterator it=begin();it!=end();++it){
        hiter hit=h.find((*it).getClass());
        if(hit!=h.end()){
            hit->second++;
        }else{
            h.insert(std::pair<_T, double>((*it).getClass(),1));
        }
    }
    
    for(hiter it=h.begin();it!=h.end();++it){
        it->second/=(double)numrecords;
    }
    return h;
}




//Istanzio le classi con i possibili tipi di dato
template class TrainingData<int>;
template class TrainingData<float>;
template class TrainingData<double>;
template class TrainingData<unsigned char>;